/*-------------------------------------------------------------------------
 *
 * miscadmin.h
 *	  此文件包含一般 PostgreSQL 管理和初始化内容，这些内容之前分散在以下文件中：
 *		globals.h						全局变量
 *		pdir.h							目录路径操作
 *		pinit.h							PostgreSQL 初始化
 *		pmod.h							处理模式
 *	  随着时间的推移，这里也成为了广为人知的资源限制内容的首选位置，例如 work_mem 和 check_stack_depth()。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/miscadmin.h
 *
 * NOTES
 *	  此文件中的一些信息应该被移动到其他文件中。
 *
 *-------------------------------------------------------------------------
 */
#ifndef MISCADMIN_H
#define MISCADMIN_H

#include <signal.h>

#include "datatype/timestamp.h" /* 针对 TimestampTz */
#include "pgtime.h"				/* 针对 pg_time_t */


#define InvalidPid				(-1)



/*****************************************************************************
 *	  系统中断和关键区处理
 *
 * 运行中的后端需要接受两种类型的中断，而不破坏其状态：QueryCancel (SIGINT) 和 ProcDie (SIGTERM)。
 * 在这两种情况下，我们需要能够优雅地清理当前事务，因此我们不能立即响应中断 ---
 * 如果代码在任意时刻被中断，则无法确保内部数据结构是自我一致的。
 * 相反，信号处理程序设置的标志在执行过程中定期检查。
 *
 * CHECK_FOR_INTERRUPTS() 宏在战略性的位置被调用，通常在这些位置接受取消或终止中断是安全的。
 * 在某些情况下，我们在可能在不希望允许取消或终止中断的上下文中被调用的低级子例程中调用 CHECK_FOR_INTERRUPTS()。
 * HOLD_INTERRUPTS() 和 RESUME_INTERRUPTS() 宏允许代码确保不会接受取消或终止中断，
 * 即使在子例程中调用了 CHECK_FOR_INTERRUPTS()。中断将被暂时屏蔽，直到 CHECK_FOR_INTERRUPTS() 在任何
 * HOLD_INTERRUPTS() ... RESUME_INTERRUPTS() 部分之外完成。
 *
 * 还有一个机制来防止查询取消中断，同时仍允许终止中断：HOLD_CANCEL_INTERRUPTS() 和
 * RESUME_CANCEL_INTERRUPTS()。
 *
 * 请注意，ProcessInterrupts() 还获得了一些不一定会导致查询取消或终止响应的任务。
 * 因此，可能它只会清除 InterruptPending 并返回。
 *
 * 可以检查 INTERRUPTS_PENDING_CONDITION() 以查看是否需要处理中断，而无需立即尝试这样做。
 * 一些调用者也对 INTERRUPTS_CAN_BE_PROCESSED() 感兴趣，它告诉我们 ProcessInterrupts 是否能确保清除中断。
 *
 * 使用特殊机制允许在等待锁或等待命令输入时接受中断（当然，仅在中断延迟计数器为零时）。请参见相关代码以获取详细信息。
 *
 * 丢失连接的处理方式类似，但连接丢失不会引发信号，而是在我们无法写入套接字时检测到。
 * 如果有一个破损连接的信号，我们可以通过在信号处理程序中设置 ClientConnectionLost 来利用它。
 *
 * 一个相关的，但概念上不同的机制是“关键区”机制。关键区不仅阻止取消/终止中断，
 * 还使任何 ereport(ERROR) 或 ereport(FATAL) 变为 ereport(PANIC) --- 即，强制进行系统范围的重置。
 * 不用说，只有真正“关键”的代码应该标记为关键区！目前，这个机制仅用于 XLOG 相关代码。
 *
 *****************************************************************************/

/* 在 globals.c 中 */
/* 这些被标记为 volatile，因为它们是由信号处理程序设置的： */
extern PGDLLIMPORT volatile sig_atomic_t InterruptPending;
extern PGDLLIMPORT volatile sig_atomic_t QueryCancelPending;
extern PGDLLIMPORT volatile sig_atomic_t ProcDiePending;
extern PGDLLIMPORT volatile sig_atomic_t IdleInTransactionSessionTimeoutPending;
extern PGDLLIMPORT volatile sig_atomic_t IdleSessionTimeoutPending;
extern PGDLLIMPORT volatile sig_atomic_t ProcSignalBarrierPending;
extern PGDLLIMPORT volatile sig_atomic_t LogMemoryContextPending;
extern PGDLLIMPORT volatile sig_atomic_t IdleStatsUpdateTimeoutPending;

extern PGDLLIMPORT volatile sig_atomic_t CheckClientConnectionPending;
extern PGDLLIMPORT volatile sig_atomic_t ClientConnectionLost;

/* 这些被标记为 volatile，因为它们是被信号处理程序检查的： */
extern PGDLLIMPORT volatile uint32 InterruptHoldoffCount;
extern PGDLLIMPORT volatile uint32 QueryCancelHoldoffCount;
extern PGDLLIMPORT volatile uint32 CritSectionCount;

/* 在 tcop/postgres.c 中 */
extern void ProcessInterrupts(void);

/* 测试中断是否挂起 */
#ifndef WIN32
#define INTERRUPTS_PENDING_CONDITION() \
	(unlikely(InterruptPending))
#else
#define INTERRUPTS_PENDING_CONDITION() \
	(unlikely(UNBLOCKED_SIGNAL_QUEUE()) ? pgwin32_dispatch_queued_signals() : 0, \
	 unlikely(InterruptPending))
#endif

/* 服务中断，如果有待处理的并且现在安全进行服务 */
#define CHECK_FOR_INTERRUPTS() \
do { \
	if (INTERRUPTS_PENDING_CONDITION()) \
		ProcessInterrupts(); \
} while(0)

/* ProcessInterrupts() 是否保证清除 InterruptPending？ */
#define INTERRUPTS_CAN_BE_PROCESSED() \
	(InterruptHoldoffCount == 0 && CritSectionCount == 0 && \
	 QueryCancelHoldoffCount == 0)

#define HOLD_INTERRUPTS()  (InterruptHoldoffCount++)

#define RESUME_INTERRUPTS() \
do { \
	Assert(InterruptHoldoffCount > 0); \
	InterruptHoldoffCount--; \
} while(0)

#define HOLD_CANCEL_INTERRUPTS()  (QueryCancelHoldoffCount++)

#define RESUME_CANCEL_INTERRUPTS() \
do { \
	Assert(QueryCancelHoldoffCount > 0); \
	QueryCancelHoldoffCount--; \
} while(0)

#define START_CRIT_SECTION()  (CritSectionCount++)

#define END_CRIT_SECTION() \
do { \
	Assert(CritSectionCount > 0); \
	CritSectionCount--; \
} while(0)


/*****************************************************************************
 *	  globals.h --															 *
 *****************************************************************************/

/*
 * 来自 utils/init/globals.c
 */
extern PGDLLIMPORT pid_t PostmasterPid;
extern PGDLLIMPORT bool IsPostmasterEnvironment;
extern PGDLLIMPORT bool IsUnderPostmaster;
extern PGDLLIMPORT bool IsBackgroundWorker;
extern PGDLLIMPORT bool IsBinaryUpgrade;

extern PGDLLIMPORT bool ExitOnAnyError;

extern PGDLLIMPORT char *DataDir;
extern PGDLLIMPORT int data_directory_mode;

extern PGDLLIMPORT int NBuffers;
extern PGDLLIMPORT int MaxBackends;
extern PGDLLIMPORT int MaxConnections;
extern PGDLLIMPORT int max_worker_processes;
extern PGDLLIMPORT int max_parallel_workers;

extern PGDLLIMPORT int MyProcPid;
extern PGDLLIMPORT pg_time_t MyStartTime;
extern PGDLLIMPORT TimestampTz MyStartTimestamp;
extern PGDLLIMPORT struct Port *MyProcPort;
extern PGDLLIMPORT struct Latch *MyLatch;
extern PGDLLIMPORT int32 MyCancelKey;
extern PGDLLIMPORT int MyPMChildSlot;

extern PGDLLIMPORT char OutputFileName[];
extern PGDLLIMPORT char my_exec_path[];
extern PGDLLIMPORT char pkglib_path[];

#ifdef EXEC_BACKEND
extern PGDLLIMPORT char postgres_exec_path[];
#endif

/*
 * 目前在 storage/backendid.h 中完成。
 *
 * extern BackendId    MyBackendId;
 */
extern PGDLLIMPORT Oid MyDatabaseId;

extern PGDLLIMPORT Oid MyDatabaseTableSpace;

/*
 * 日期/时间配置
 *
 * DateStyle 定义了日期/时间类型的输出格式选择：
 *	USE_POSTGRES_DATES 指定传统的 Postgres 格式
 *	USE_ISO_DATES 指定符合 ISO 的格式
 *	USE_SQL_DATES 指定符合 Oracle/Ingres 的格式
 *	USE_GERMAN_DATES 指定德国风格的 dd.mm/yyyy
 *
 * DateOrder 定义了在读取模糊日期时假定的字段顺序
 *（任何不在 YYYY-MM-DD 格式的东西，且四位数年份字段在前的，都被视为模糊）：
 *	DATEORDER_YMD 指定字段顺序 yy-mm-dd
 *	DATEORDER_DMY 指定字段顺序 dd-mm-yy（"欧洲"惯例）
 *	DATEORDER_MDY 指定字段顺序 mm-dd-yy（"美国"惯例）
 *
 * 在 Postgres 和 SQL DateStyles 中，DateOrder 也选择输出字段顺序：
 * 在 DMY 风格中，日子在月子前面，否则月子在日子前面。
 *
 * 用户可见的 "DateStyle" 运行时参数包含这两个配置。
 */

/* 有效的 DateStyle 值 */
#define USE_POSTGRES_DATES		0
#define USE_ISO_DATES			1
#define USE_SQL_DATES			2
#define USE_GERMAN_DATES		3
#define USE_XSD_DATES			4

/* 有效的 DateOrder 值 */
#define DATEORDER_YMD			0
#define DATEORDER_DMY			1
#define DATEORDER_MDY			2

extern PGDLLIMPORT int DateStyle;
extern PGDLLIMPORT int DateOrder;

/*
 * IntervalStyles
 *	 INTSTYLE_POSTGRES			   类似于 Postgres < 8.4 当 DateStyle = 'iso'
 *	 INTSTYLE_POSTGRES_VERBOSE	   类似于 Postgres < 8.4 当 DateStyle != 'iso'
 *	 INTSTYLE_SQL_STANDARD		   SQL 标准的间隔文字
 *	 INTSTYLE_ISO_8601			   ISO-8601-basic 格式的间隔
 */
#define INTSTYLE_POSTGRES			0
#define INTSTYLE_POSTGRES_VERBOSE	1
#define INTSTYLE_SQL_STANDARD		2
#define INTSTYLE_ISO_8601			3

extern PGDLLIMPORT int IntervalStyle;

#define MAXTZLEN		10		/* 最大 TZ 名称长度，不包括 tr. null */

extern PGDLLIMPORT bool enableFsync;
extern PGDLLIMPORT bool allowSystemTableMods;
extern PGDLLIMPORT int work_mem;
extern PGDLLIMPORT double hash_mem_multiplier;
extern PGDLLIMPORT int maintenance_work_mem;
extern PGDLLIMPORT int max_parallel_maintenance_workers;

extern PGDLLIMPORT int VacuumCostPageHit;
extern PGDLLIMPORT int VacuumCostPageMiss;
extern PGDLLIMPORT int VacuumCostPageDirty;
extern PGDLLIMPORT int VacuumCostLimit;
extern PGDLLIMPORT double VacuumCostDelay;

extern PGDLLIMPORT int64 VacuumPageHit;
extern PGDLLIMPORT int64 VacuumPageMiss;
extern PGDLLIMPORT int64 VacuumPageDirty;

extern PGDLLIMPORT int VacuumCostBalance;
extern PGDLLIMPORT bool VacuumCostActive;


/* 在 tcop/postgres.c 中 */

#if defined(__ia64__) || defined(__ia64)
typedef struct
{
	char	   *stack_base_ptr;
	char	   *register_stack_base_ptr;
} pg_stack_base_t;
#else
typedef char *pg_stack_base_t;
#endif

extern pg_stack_base_t set_stack_base(void);
extern void restore_stack_base(pg_stack_base_t base);
extern void check_stack_depth(void);
extern bool stack_is_too_deep(void);

/* 在 tcop/utility.c 中 */
extern void PreventCommandIfReadOnly(const char *cmdname);
extern void PreventCommandIfParallelMode(const char *cmdname);
extern void PreventCommandDuringRecovery(const char *cmdname);

/* 在 utils/misc/guc.c 中 */
extern PGDLLIMPORT int trace_recovery_messages;
extern int	trace_recovery(int trace_level);

/*****************************************************************************
 *	  pdir.h --																 *
 *			POSTGRES 目录路径定义。                                         *
 *****************************************************************************/

/* 用于形成 sec_context 的标志 */
#define SECURITY_LOCAL_USERID_CHANGE	0x0001
#define SECURITY_RESTRICTED_OPERATION	0x0002
#define SECURITY_NOFORCE_RLS			0x0004

extern PGDLLIMPORT char *DatabasePath;

/* 现在在 utils/init/miscinit.c 中 */
extern void InitPostmasterChild(void);
extern void InitStandaloneProcess(const char *argv0);
extern void SwitchToSharedLatch(void);
extern void SwitchBackToLocalLatch(void);

typedef enum BackendType
{
	B_INVALID = 0,
	B_AUTOVAC_LAUNCHER,
	B_AUTOVAC_WORKER,
	B_BACKEND,
	B_BG_WORKER,
	B_BG_WRITER,
	B_CHECKPOINTER,
	B_STARTUP,
	B_WAL_RECEIVER,
	B_WAL_SENDER,
	B_WAL_WRITER,
	B_ARCHIVER,
	B_LOGGER,
} BackendType;

extern PGDLLIMPORT BackendType MyBackendType;

#define AmRegularBackendProcess()	(MyBackendType == B_BACKEND)

extern const char *GetBackendTypeDesc(BackendType backendType);

extern void SetDatabasePath(const char *path);
extern void checkDataDir(void);
extern void SetDataDir(const char *dir);
extern void ChangeToDataDir(void);

extern char *GetUserNameFromId(Oid roleid, bool noerr);
extern Oid	GetUserId(void);
extern Oid	GetOuterUserId(void);
extern Oid	GetSessionUserId(void);
extern bool GetSessionUserIsSuperuser(void);
extern Oid	GetAuthenticatedUserId(void);
extern bool GetAuthenticatedUserIsSuperuser(void);
extern void SetAuthenticatedUserId(Oid userid, bool is_superuser);
extern void GetUserIdAndSecContext(Oid *userid, int *sec_context);
extern void SetUserIdAndSecContext(Oid userid, int sec_context);
extern bool InLocalUserIdChange(void);
extern bool InSecurityRestrictedOperation(void);
extern bool InNoForceRLSOperation(void);
extern void GetUserIdAndContext(Oid *userid, bool *sec_def_context);
extern void SetUserIdAndContext(Oid userid, bool sec_def_context);
extern void InitializeSessionUserId(const char *rolename, Oid useroid);
extern void InitializeSessionUserIdStandalone(void);
extern void SetSessionAuthorization(Oid userid, bool is_superuser);
extern Oid	GetCurrentRoleId(void);
extern void SetCurrentRoleId(Oid roleid, bool is_superuser);

/* 在 utils/misc/superuser.c 中 */
extern bool superuser(void);	/* 当前用户是超级用户 */
extern bool superuser_arg(Oid roleid);	/* 给定用户是超级用户 */


/*****************************************************************************
 *	  pmod.h --																 *
 *			POSTGRES 处理模式定义。                                         *
 *****************************************************************************/

/*
 * 描述：
 *		POSTGRES 中有三种处理模式。它们是
 * 启动处理或“bootstrap”，初始化处理或
 * “initialization”，以及正常处理或“normal”。
 *
 * 前两种处理模式在特殊时间使用。当
 * 系统状态指示启动处理时，所有事务都被赋予
 * 事务 ID “one”，因此保证可以提交。此模式
 * 用于模板数据库的初始生成。
 *
 * 初始化模式：在启动后端时使用，直到所有正常
 * 初始化完成。某些代码在此模式下执行时表现
 * 不同，以启用系统启动。
 *
 * 如果 POSTGRES 后端进程处于正常模式，则所有代码可能
 * 正常执行。
 */

typedef enum ProcessingMode
{
	BootstrapProcessing,		/* 启动模板数据库的创建 */
	InitProcessing,				/* 初始化系统 */
	NormalProcessing			/* 正常处理 */
} ProcessingMode;

extern PGDLLIMPORT ProcessingMode Mode;

#define IsBootstrapProcessingMode() (Mode == BootstrapProcessing)
#define IsInitProcessingMode()		(Mode == InitProcessing)
#define IsNormalProcessingMode()	(Mode == NormalProcessing)

#define GetProcessingMode() Mode

#define SetProcessingMode(mode) \
	do { \
		AssertArg((mode) == BootstrapProcessing || \
				  (mode) == InitProcessing || \
				  (mode) == NormalProcessing); \
		Mode = (mode); \
	} while(0)


/*
 * 辅助进程类型标识符。这些以前在 bootstrap.h 中
 * 但是将它们放在这里，与 ProcessingMode 相关内容一起
 * 似乎更合理。MyAuxProcType 全局在 auxprocess.c 中定义和设置。
 *
 * 确保在词汇表中列出您在这里添加的任何项目。
 */

typedef enum
{
	NotAnAuxProcess = -1,
	StartupProcess = 0,
	BgWriterProcess,
	ArchiverProcess,
	CheckpointerProcess,
	WalWriterProcess,
	WalReceiverProcess,

	NUM_AUXPROCTYPES			/* 必须是最后一个！ */
} AuxProcType;

extern PGDLLIMPORT AuxProcType MyAuxProcType;

#define AmStartupProcess()			(MyAuxProcType == StartupProcess)
#define AmBackgroundWriterProcess() (MyAuxProcType == BgWriterProcess)
#define AmArchiverProcess()			(MyAuxProcType == ArchiverProcess)
#define AmCheckpointerProcess()		(MyAuxProcType == CheckpointerProcess)
#define AmWalWriterProcess()		(MyAuxProcType == WalWriterProcess)
#define AmWalReceiverProcess()		(MyAuxProcType == WalReceiverProcess)


/*****************************************************************************
 *	  pinit.h --															 *
 *			POSTGRES 初始化和清理定义。                                 *
 *****************************************************************************/

/* 在 utils/init/postinit.c 中 */
extern void pg_split_opts(char **argv, int *argcp, const char *optstr);
extern void InitializeMaxBackends(void);
extern void InitPostgres(const char *in_dbname, Oid dboid,
						 const char *username, Oid useroid,
						 bool load_session_libraries,
						 bool override_allow_connections,
						 char *out_dbname);
extern void BaseInit(void);

/* 在 utils/init/miscinit.c 中 */
extern PGDLLIMPORT bool IgnoreSystemIndexes;
extern PGDLLIMPORT bool process_shared_preload_libraries_in_progress;
extern PGDLLIMPORT bool process_shared_preload_libraries_done;
extern PGDLLIMPORT bool process_shmem_requests_in_progress;
extern PGDLLIMPORT char *session_preload_libraries_string;
extern PGDLLIMPORT char *shared_preload_libraries_string;
extern PGDLLIMPORT char *local_preload_libraries_string;

extern void CreateDataDirLockFile(bool amPostmaster);
extern void CreateSocketLockFile(const char *socketfile, bool amPostmaster,
								 const char *socketDir);
extern void TouchSocketLockFiles(void);
extern void AddToDataDirLockFile(int target_line, const char *str);
extern bool RecheckDataDirLockFile(void);
extern void ValidatePgVersion(const char *path);
extern void process_shared_preload_libraries(void);
extern void process_session_preload_libraries(void);
extern void process_shmem_requests(void);
extern void pg_bindtextdomain(const char *domain);
extern bool has_rolreplication(Oid roleid);

typedef void (*shmem_request_hook_type) (void);
extern PGDLLIMPORT shmem_request_hook_type shmem_request_hook;

/* 在 executor/nodeHash.c 中 */
extern size_t get_hash_memory_limit(void);

#endif							/* MISCADMIN_H */
